package org.carl.servlet;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalTime;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.carl.core.ContentType;

public class CarlServlet extends HttpServlet {
	private static final Logger LOG = Logger.getGlobal();
	private static final long serialVersionUID = 1L;
	private static final String PRE = "/WEB-INF/views/", FIX = ".jsp";

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String encod = "UTF-8";
		request.setCharacterEncoding(encod);
		response.setCharacterEncoding(encod);
	}

	/**
	 * 执行目标方法
	 * 
	 * @param request
	 *            HttpServletRequest
	 * @param response
	 *            HttpServletResponse
	 * @param method
	 *            Method 对应方法
	 * @param target
	 *            Object
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws ServletException
	 * @throws IOException
	 * @throws FileNotFoundException
	 * @throws IntrospectionException
	 */
	protected void executeTarget(HttpServletRequest request, HttpServletResponse response, Object target, Method method)
			throws IllegalAccessException, InvocationTargetException, ServletException, IOException,
			FileNotFoundException, IntrospectionException {
		Object res;
		try {
			res = method.invoke(target);
		} catch (IllegalArgumentException e) {
			res = method.invoke(target, request, response);
		}
		LOG.info("方法无页面返回...");
		// 方法无返回,可能是方法内部处理.
		if (Objects.isNull(res)) {
			return;
		}
		if (res.getClass() != ContentType.class) {
			LOG.info("方法返回异常");
		} else {
			// 自动注入model模型
			ContentType ct = (ContentType) res;
			if (Objects.nonNull(ct.getModel())) {
				// 设置model数据
				ct.getModel().entrySet().stream().forEach(e -> {
					request.setAttribute(e.getKey(), e.getValue());
				});
			}
			// 自动注入属性
			BeanInfo bi = Introspector.getBeanInfo(target.getClass());
			for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
				request.setAttribute(pd.getName(), pd.getValue(pd.getName()));
			}
			switch (ct) {
			case PAGE:
				if(Objects.isNull(ct.getContent())){
					ct.setContent(method.getName());
				}
				request.getRequestDispatcher(
						PRE + Optional.of(ct.getContent().toString()).orElse(method.getName()) + FIX)
						.forward(request, response);
				break;
			case JSON:
				response.setContentType("application/json");
				print(response, ct);
				break;
			case XML:
				response.setContentType("application/xml");
				print(response, ct);
				break;
			case FILE:
				response.setContentType("application/octet-stream");
				ServletOutputStream out = response.getOutputStream();
				InputStream is = null;
				if(ct.getContent() instanceof String){
					is=new FileInputStream(new File(ct.getContent().toString()));
				}else if(ct.getContent() instanceof InputStream){
					is=(InputStream) ct.getContent();
				}
				// 3.设置让浏览器不进行缓存，不然会发现下载功能在opera和firefox里面好好的没问题，在IE下面就是不行，就是找不到文件
				response.setHeader("Pragma", "No-cache");
				response.setHeader("Cache-Control", "No-cache");
				response.setDateHeader("Expires", -1);
				// 4.设置Content-type字段
				response.setContentType("image/jpeg");
				// 5.设置http响应头，告诉浏览器以下载的方式处理我们的响应信息
				response.setHeader("content-disposition", "attachment;filename=" + LocalTime.now().toString()
						+ ct.getContent().toString().substring(ct.getContent().toString().lastIndexOf(".")));
				// 6.开始写文件
				byte[] buf = new byte[1024];
				int len = 0;
				while ((len = is.read(buf)) != -1) {
					out.write(buf, 0, len);
				}
				if (is != null) {
					is.close();
				}
				break;
			}
		}
	}

	private void print(HttpServletResponse response, ContentType ct) throws IOException {
		PrintWriter pw = response.getWriter();
		pw.write(ct.getContent().toString());
		pw.close();
	}
/**
 * 初始化自动注入前端同名属性和map类型名称叫parameters包含所有前端传入参数
 * @param instance Object
 * @param parameters Map<String, String[]>
 * @throws IntrospectionException
 * @throws IllegalAccessException
 * @throws IllegalArgumentException
 * @throws InvocationTargetException
 */
	protected void initialFields(Object instance, Map<String, String[]> parameters)
			throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		BeanInfo bi = Introspector.getBeanInfo(instance.getClass());
		// 设置属性值
		for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
			//如果属性中有parameters类型的map会自动注入
			if(pd.getPropertyType()==Map.class&&pd.getName().equals("parameters")){
				pd.getWriteMethod().invoke(instance, parameters);
			}
			if (parameters.containsKey(pd.getName())) {
				Object data = null;
				if (pd.getPropertyType().isArray()) {
					// 需要获取集合
					data = parameters.get(parameters.get(pd.getName()));
				} else {
					// 仅仅需要获取一个
					data = parameters.get(parameters.get(pd.getName()))[0];
				}
				pd.getWriteMethod().invoke(instance, data);
			}
		}
	}
}
